package com.sfeir.poc.portal.gui.client.dnd;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.WindowResizeListener;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabBar;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sfeir.poc.portal.gui.client.Application;
import com.sfeir.poc.portal.gui.client.events.ApplicationEvent;
import com.sfeir.poc.portal.gui.client.events.ApplicationEventListener;
import com.sfeir.poc.portal.gui.client.events.ApplicationEventProvider;
import com.sfeir.poc.portal.gui.client.events.ApplicationEventType;
import com.sfeir.poc.portal.gui.client.objects.Position;
import com.sfeir.poc.portal.gui.client.objects.WidgetDescription;
import com.sfeir.poc.portal.gui.client.services.feed.Entrie;
import com.sfeir.poc.portal.gui.client.services.feed.Feed;

/**
 * Class that draws widgets and make them draggable
 * @author bostoen.v
 *
 */
public final class IndexedPanel extends SimplePanel implements ApplicationEventProvider, ApplicationEventListener, HistoryListener, WindowResizeListener{

	private final List<ApplicationEventListener> applicationListeners = new ArrayList<ApplicationEventListener>();
	private static final int SPACING = 0;

	private Map<String, String> sheetsUrls = null;

	private AbsolutePanel boundaryPanel = null;
	private Panel mainContainer = null;
	private TabBar bar = null;
	private HorizontalPanel horizontalPanel = null;

	private PickupDragController widgetDragController = null;

	private List<NoInsertAtEndIndexedDropController> widgetDropControllerList = null;
	private List<Label> spacerLabelList = null;
	private List<VerticalPanel> columnCompositePanelList = null;
	private List<VerticalPanel> verticalPanelList = null;

	private HTML aboutLabel = null;

	public IndexedPanel() {
		History.addHistoryListener(this);
		Window.addWindowResizeListener(this);
		addStyleName("indexed-panel");

		sheetsUrls = new HashMap<String, String>();

		widgetDropControllerList = new ArrayList<NoInsertAtEndIndexedDropController>();
		spacerLabelList = new ArrayList<Label>();
		columnCompositePanelList = new ArrayList<VerticalPanel>();

		boundaryPanel = new AbsolutePanel();
		setWidget(boundaryPanel);
		mainContainer = new VerticalPanel();

		// use the boundary panel as this composite's widget
		boundaryPanel.addStyleName("main-container");
		boundaryPanel.setSize("97%", "100%");

		//initialize main container

		mainContainer.setSize("100%", "100%");
		boundaryPanel.add(mainContainer);

		HorizontalPanel headerPanel = new HorizontalPanel();
		headerPanel.addStyleName("header-panel");
		headerPanel.setSize("100%", "75px");

		String title = Application.getDefault().getArgs("title");

		headerPanel.add(new Label(title));
		mainContainer.add(headerPanel);

		bar = new TabBar();

		bar.addTabListener(new TabListener(){
			public boolean onBeforeTabSelected(SourcesTabEvents sender, int tabIndex) {
				return true;
			}

			public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
				History.newItem(bar.getTabHTML(tabIndex));
			}
		});

		Grid subHeaderGrid = new Grid();
		subHeaderGrid.resize(1, 2);
		subHeaderGrid.setWidth("100%");
		subHeaderGrid.setWidget(0, 0, bar);
		aboutLabel = new HTML("<a href=\"" + Application.getDefault().getArgs("spreadsheet") + "\" target=\"_blank\">About</a>");
		aboutLabel.addStyleName("about-Label");

		subHeaderGrid.setWidget(0, 1, aboutLabel);
		subHeaderGrid.getCellFormatter().setHorizontalAlignment(0, 1, HasHorizontalAlignment.ALIGN_RIGHT);
		mainContainer.add(subHeaderGrid);

		widgetDragController = new PickupDragController(boundaryPanel, false);
		widgetDragController.setBehaviorMultipleSelection(false);

		verticalPanelList = new ArrayList<VerticalPanel>();
		for (int i = 0; i < 3; i++) {
			VerticalPanel verticalPanel = new VerticalPanel();
			verticalPanel.setSpacing(SPACING);
			verticalPanelList.add(verticalPanel);

			NoInsertAtEndIndexedDropController widgetDropController = new NoInsertAtEndIndexedDropController(verticalPanelList.get(i));
			widgetDragController.registerDropController(widgetDropController);
			widgetDropControllerList.add(widgetDropController);

			Label spacerLabel = new Label("");
			spacerLabel.setSize((Window.getClientWidth()/3) - 30 + "px", "100px");
			spacerLabelList.add(spacerLabel);

			VerticalPanel columnCompositePanel = new VerticalPanel();
			columnCompositePanel.setWidth((Window.getClientWidth()/3) -30 + "px");
			columnCompositePanel.addStyleName("column");
			columnCompositePanelList.add(columnCompositePanel);
		}

		// initialize horizontal panel to hold our columns
		horizontalPanel = new HorizontalPanel();
		horizontalPanel.setSpacing(SPACING);
		horizontalPanel.setWidth("100%");
		mainContainer.add(horizontalPanel);
	}

	/**
	 * Just used to init the panel with the first tab
	 * TODO:
	 * -delete this function
	 */
	public void init()
	{
		fireActionEvent(ApplicationEventType.GET_TAB_TITLE, getBootstrapSpreadsheetKey());
	}

	/**
	 * Function that get the spreadsheet key value from a JavaScript variable declared inside
	 * the NouvelleCuisine.html file
	 * @return the spreadsheet id
	 */
	private native String getBootstrapSpreadsheetKey()/*-{
		return($wnd.spreadsheetId);
 	}-*/;

	/**
	 * Function that returns the spreadsheet url 
	 * that correspond to the tab index passed as a parameter
	 * @param num
	 * @return the url of the spreadsheet
	 */
	public native String getBootstrapSpreadsheet(int num)/*-{
		return($wnd.spreadsheetUrls[num]);
 	}-*/;

	/**
	 * This function is called when informations has been retrieved from spreadsheet
	 * It will init the screen with the corresponding widgets
	 * @param widgets2
	 */
	public void drawScreen(HashMap<Position, WidgetDescription> widgets2) {

		horizontalPanel.clear();
		horizontalPanel.setWidth("100%");
		for (int i = 0; i < 3; i++) {
			verticalPanelList.get(i).clear();
		}
		addStyleName("indexed-panel");

		for (int i = 0; i < 3; i++) {
			horizontalPanel.add(columnCompositePanelList.get(i));
			columnCompositePanelList.get(i).add(verticalPanelList.get(i));
		}

		for (Iterator<Position> iterator = widgets2.keySet().iterator(); iterator.hasNext();) {
			Position position = (Position) iterator.next();

			WidgetDescription currentWidget = widgets2.get(position);

			WindowPanel widget = createWidget(currentWidget);
			drawWidget(widget, currentWidget.getColumn());
		}
		for (int i = 0; i < 3; i++) {
			verticalPanelList.get(i).add(spacerLabelList.get(i));
		}
	}

	/**
	 * This function creates a visual widget corresponding to a widget description
	 * and make it draggable
	 * @param widgetDescription
	 * @return the corresponding window panel
	 */
	private WindowPanel createWidget(WidgetDescription widgetDescription){
		HTML widgetHeader = new HTML("Widget");
		widgetHeader.setSize("100%", "50px");
		Panel widgetContent = new HorizontalPanel();

		widgetContent.add(widgetDescription.getWidgetRepresentation());

		widgetHeader.setHTML(widgetDescription.getTitle());

		widgetContent.addStyleName("demo-resize-html");

		WindowPanel widget = new WindowPanel(widgetDropControllerList.get(widgetDescription.getColumn()-1), widgetHeader, widgetContent, true);
		widgetDragController.makeDraggable(widget, widgetHeader);
		return widget; 
	}

	/**
	 * This function adds a created widget inside a column from
	 * the drag and drop panel
	 * @param widget
	 * @param colonne
	 */
	private void drawWidget(WindowPanel widget, int colonne){
		colonne--;
		verticalPanelList.get(colonne).remove(spacerLabelList.get(colonne));
		boundaryPanel.add(widget, 70, 30);
		verticalPanelList.get(colonne).add(widget);
		verticalPanelList.get(colonne).add(spacerLabelList.get(colonne));
	}

	
	private void updateWidget(WidgetDescription widgetDescription){
		drawWidget(createWidget(widgetDescription), widgetDescription.getColumn());
	}

	public void addApplicationEventListener(final ApplicationEventListener listener) {
		if (!this.applicationListeners.contains(listener)) {
			this.applicationListeners.add(listener);

			if (listener instanceof ApplicationEventProvider) {
				((ApplicationEventProvider) listener).addApplicationEventListener(this); 
			}
		}
	}

	public void removeApplicationEventListener(final ApplicationEventListener listener) {
		if (this.applicationListeners.contains(listener)) {
			this.applicationListeners.remove(listener);

			if (listener instanceof ApplicationEventProvider) {
				((ApplicationEventProvider) listener).removeApplicationEventListener(this); 
			}
		}
	}

	public void fireActionEvent(final ApplicationEventType type, final Object eventContext) {
		final ApplicationEvent event = new ApplicationEvent(type, eventContext);

		for (ApplicationEventListener listener : this.applicationListeners) {
			listener.onActionFired(event);
		}
	}

	@SuppressWarnings("unchecked")
	public void onActionFired(ApplicationEvent event) {
		if(event.getType().equals(ApplicationEventType.DISPLAY_WIDGETS)){
			drawScreen((HashMap<Position, WidgetDescription>) event.getContext());
		}else if(event.getType().equals(ApplicationEventType.DISPLAY_WIDGET)){
			updateWidget((WidgetDescription) event.getContext());
		}else if(event.getType().equals(ApplicationEventType.UPDATE_TAB)){
			updateTabText((Feed)event.getContext());
		}
	}

	/**
	 * This function is called when sheets title are retrieved
	 * It change the tabs text to match sheets title
	 * @param context
	 */
	private void updateTabText(Feed context) {
		int i = 0;
		for (Iterator<Entrie> iterator = context.getEntries().iterator(); iterator.hasNext();) {
			Entrie sheet = (Entrie) iterator.next();
			bar.addTab(sheet.getTitle());
			sheetsUrls.put(sheet.getTitle(), getBootstrapSpreadsheet(i));
			i++;
		}
		if(bar.getTabCount() > 0)
			bar.selectTab(0);
	}

	public void onHistoryChanged(String historyToken) {
		if(sheetsUrls == null)
			return;
		fireActionEvent(ApplicationEventType.RETRIEVE_WIDGETS, sheetsUrls.get(historyToken) );
	}

	/**
	 * Called when the Client Browser Window is resized
	 * Used to adapt the widget container size
	 */
	public void onWindowResized(int width, int height) {
		for (Iterator<VerticalPanel> iterator = columnCompositePanelList.iterator(); iterator.hasNext();) {
			VerticalPanel currentColumn = (VerticalPanel) iterator.next();
			currentColumn.setWidth((width/3) - 30 + "px");
		}
		for (Iterator<Label> iterator = spacerLabelList.iterator(); iterator.hasNext();) {
			Label currentSpacer = (Label) iterator.next();
			currentSpacer.setWidth((Window.getClientWidth()/3) - 30 + "px");
		}
	}
}
